# encoding:utf-8
'''
@author : xaing
@time : 2019/4/4 10:13
@地址 : 广州
@作用 : web 常用方法
@结果 :
'''

# 随机
import random
from urllib import parse,request
from version1 import common
import json
from version1 import logUtils as log
import requests
from requests_toolbelt import MultipartEncoder
import os
from http import cookiejar
import chardet






def getUserAgent():
    '''
    随机返回一个 User-Agent 请求头，来模拟浏览器访问,请求头可以自己继续添加数据
    :return:  {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0'}
    '''
    user_agents = [
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60',
        'Opera/8.0 (Windows NT 5.1; U; en)',
        'Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50',
        'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.50',
        'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0',
        'Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2 ',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36',
        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',
        'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36',
        'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER',
        'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)',
        'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 SE 2.X MetaSr 1.0',
        'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; SE 2.X MetaSr 1.0) ',
    ]
    # random.choice(),从列表中随机抽取一个对象
    user_agent = random.choice(user_agents)
    header = {'User-Agent': user_agent}
    return header



def get(url, parm):
    '''
    发送get请求
    :param url: 访问的URL地址
    :param parm: 参数，示例 {'count': 20}
    :return:
    '''
    if parm is not None:
        parm = parse.urlencode(parm)
    # 转换{'user':'admin','password':'admin'}类型参数为user=admin&password=admin
    # common.printStr("输出参数", parm)
    header = getUserAgent()
    req = request.Request(url='%s%s%s' % (url, '?', parm), headers= header)
    res = request.urlopen(req)
    res = res.read()
    # 输出内容(python3默认获取到的是16进制'bytes'类型数据Unicode编码
    html = res.decode(encoding= 'utf-8')

    return html


def postType(url, parm, type, filePath):
    '''
    post 形式请求
    :param url: 请求的地址
    :param type: sr 标准形式   file 文件形式，需要自定义参数  json json数据形式  xml xml形式
    :param parm:  字典形式 {"user":"admin","password":"zabbix"}
    :return: 网页内容或请求的返回值
    '''
    if common.checkNull(url):
        raise Exception("请求地址不能为空")

    if common.checkNull(type):
        raise Exception("类型不能为空")

    # 拿到请求头
    header = getUserAgent()

    if "sr" is type:
        # 通用方式
        # application / x - www - form - urlencoded,最常见的 POST 提交数据的方式了。浏览器的原生 form 表单，如果不设置 enctype 属性，那么最终就会
        # 以 application/x-www-form-urlencoded 方式提交数据。
        # 数据传输形式：requests.post(url='',data={'key1':'value1','key2':'value2'},headers={'Content-Type':'application/x-www-form-urlencoded'})
        header['Content-Type'] = 'application/x-www-form-urlencoded'
        res = requests.post(url, data= parm, headers= header)
        res = res.text
        return res

    elif "file" is type:
        # 发送文件中的数据需要（安装requests_toolbelt）  pip install requests_toolbelt
        # multipart / form - data，经常用到的是上传文件用的表单，这又是一个常见的 POST 数据提交的方式。我们使用表单上传文件时，必须让 form 的 enctyped 等于这个值
        # 数据传输形式:requests.post(url='',data={'key1':'value1','key2':'value2'},headers={'Content-Type':'multipart/form-data'})
        header['Content-Type'] = 'multipart/form-data'

        if common.checkNull(filePath):
            # TODO 无文件,需要根据情况来进行编码,比如参数值
            m = MultipartEncoder(fields={'field0': 'value', 'field1': 'value'})
            res = requests.post(url, data= m, headers={'Content-Type': m.content_type})
            return res.text

        else:
            # TODO 有文件,需要根据情况来进行编码,比如参数值
            if os.path.exists(filePath):
                m = MultipartEncoder(
                    fields={'field0': 'value', 'field1': 'value', 'field2': ('filename', open(filePath, 'rb'), 'text/plain')}
                )
                r = requests.post(url, data= m, headers= header)
                return r.text

            else:
                raise Exception("文件不存在，不能发送")

    elif "json" is type:
        # application / json这个Content - Type作为响应头大家肯定不陌生。实际上，现在越来越多的人把它作为请求头，用来告诉服务端消息主体是序列化后的JSON字符串
        # 数据传输形式：requests.post(url='',data=json.dumps({'key1':'value1','key2':'value2'}),headers={'Content-Type':'application/json'})
        header['Content-Type'] = 'application/json'
        data = json.dumps(parm)
        res = requests.post(url= url, data= data, headers= header)
        return res.text

    elif "xml" is type:
        # 一种使用HTTP作为传输协议，XML作为编码方式的远程调用规范
        # 数据传输形式：requests.post(url='',data='<?xml  ?>',headers={'Content-Type':'text/xml'})
        header['Content-Type'] = 'text/xml'
        # data中的数据是 <?xml  ?>
        res = requests.post(url='', data= parm, headers= header)
        return res.text

    else:
        raise Exception("不能识别的请求类型")


def post(url, parm):
    '''
    post请求
    :param url: 网址访问
    :param parm: 参数，字典形式 {"user":"admin","password":"zabbix"}
    :return:
    '''
    if common.checkNull(url):
        raise Exception("访问网址不能为空")

    data  = parse.urlencode(parm).encode('utf-8')
    res = request.Request(url, data, getUserAgent())
    html = request.urlopen(res).read().decode('utf-8')
    return html


def cookieLoginPost(url, parm, cookieFileName):
    '''
    用于post请求 登录的记住cookie
    :param url: 访问网址
    :param parm: 请求参数 字典形式 {"user":"admin","password":"zabbix"}
    :param cookieFileName: cookie保存路径加名字 E:\\临时\\1.txt
    :return: 返回请求结果，cookie文件放到了指定位置
    '''
    if common.checkNull(url):
        raise Exception("访问网址不能为空")
    if common.checkNull(cookieFileName):
        raise Exception("cookie保存路径不能为空")

    data = parse.urlencode(parm).encode('utf-8')
    req = request.Request(url, data, getUserAgent())

    # 保存cookie的一些代码
    # 声明一个MozillaCookieJar对象实例来保存cookie，之后写入文件
    cookieObj = cookiejar.MozillaCookieJar(cookieFileName)
    # 利用urllib.request库的HTTPCookieProcessor对象来创建cookie处理器,也就CookieHandler
    handler = request.HTTPCookieProcessor(cookieObj)

    # 自动记住cookie
    cookie = cookiejar.CookieJar()
    # opener = request.build_opener(request.HTTPCookieProcessor(cookie), request.HTTPHandler)
    opener = request.build_opener(request.HTTPCookieProcessor(cookie), handler)
    # 安装opener到全局
    request.install_opener(opener)
    # open方法打开网页
    r = opener.open(req)
    # 带cookie的打印出来必须用opener.open(req).read().decode('utf-8')来发送的请求才会带上cookie，如果用urllib.request.urlopen()是不带cookie的
    html = r.read().decode('utf-8')

    # 保存cookie到文件 ignore_discard的意思是即使cookies将被丢弃也将它保存下来； ignore_expires的意思是如果在该文件中cookies已经存在，则覆盖原文件写入
    cookieObj.save(ignore_discard=True, ignore_expires=True)

    return html



def getFileCookie(cookiePath):
    '''
    获取文件中的cookie信息
    :param cookiePath: cookie存放的路径
    :return: cookie对象
    '''
    if common.checkNull(cookiePath):
        raise Exception("cookie路径不能为空")

    if os.path.exists(cookiePath):
        cookie = cookiejar.MozillaCookieJar()
        # 从文件中读取cookie内容到变量
        cookie.load(cookiePath, ignore_discard=True, ignore_expires=True)
        return cookie

    else:
        raise Exception("cookie文件不存在")





def cookiePost(url, parm, cookiePath):
    '''
    使用固定的cookie进行post 请求
    如果已知cookie内容，且要用这个固定cookie去发送请求，可以在header中直接添加cookie内容去发送请求
    :param url:
    :param parm: 字典形式 {"user":"admin","password":"zabbix"}
    :return:
    '''
    if common.checkNull(url):
        raise Exception("访问网址不能为空")

    # 使用urlencode方法转换标准格式
    data = parse.urlencode(parm).encode('utf-8')
    req = request.Request(url, data, getUserAgent())

    cookie = getFileCookie(cookiePath)
    #利用urllib.request库的HTTPCookieProcessor对象来创建cookie处理器,也就CookieHandler
    cookie_support = request.HTTPCookieProcessor(cookie)
    #通过CookieHandler创建opener
    opener = request.build_opener(cookie_support)

    # open方法打开网页
    r = opener.open(req)

    # 带cookie的打印出来必须用opener.open(req).read().decode('utf-8')来发送的请求才会带上cookie，如果用urllib.request.urlopen()是不带cookie的
    html = r.read().decode('utf-8')

    return html



def getTimeout(url, timeout):
    '''
    简单的get请求，设置超时时间
    :param url: 请求url
    :param timeout: 超时时间，单位秒， 传入数字 5
    :return: 得到返回的请求
    '''
    res = requests.get(url, timeout= timeout)
    # 返回的状态码
    code = res.status_code
    common.printStr("返回状态码：", code)
    return res


def getEncode(html):
    '''
    获取网页的编码
    :param html:
            encode = getEncode(urllib.request.urlopen(req).read())
            urllib.request.urlopen(req).read().decode(encode)
    :return: 编码类型
    '''
    encode = chardet.detect(html)
    # 如果遇到ＧＢＫ２３１２等编码的，在ｄｅｃｏｄｅ和ｅｎｃｏｄｅ时，一律使用ＧＢＫ进行编码或者解码，这是因为ＧＢＫ是其他ＧＢＫ编码的超集，向下兼容所有的ＧＢＫ编码
    code = encode['encoding']
    if 'GB2312' is code:
        code = 'GBK'
    return code